package com.lfy.yunpicture.manager.websocket;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.lfy.yunpicture.common.ErrorCode;
import com.lfy.yunpicture.exception.BusinessException;
import com.lfy.yunpicture.exception.ThrowUtils;
import com.lfy.yunpicture.manager.websocket.disruptor.PictureEditEventProducer;
import com.lfy.yunpicture.manager.websocket.model.PictureEditActionEnum;
import com.lfy.yunpicture.manager.websocket.model.PictureEditRequestMessage;
import com.lfy.yunpicture.manager.websocket.model.PictureEditResponseMessage;
import com.lfy.yunpicture.manager.websocket.model.PictureEditTypeEnum;
import com.lfy.yunpicture.model.entity.User;
import com.lfy.yunpicture.model.vo.UserVO;
import com.lfy.yunpicture.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 图片编辑消费
 */
@Component
@Slf4j
public class PictureEditWorkHandle extends TextWebSocketHandler {
    @Resource
    private UserService userService;
    @Resource
    private PictureEditEventProducer pictureEditEventProducer;
    /**
     * key代表pictureId,value代表userId
     */
    private Map<Long, Long> pictureIdSessionIdMap = new ConcurrentHashMap<>();
    /**
     * key代表pictureId,value代表在加入编辑的session集合
     */
    private Map<Long, Set<WebSocketSession>> pictureIdSessionSetMap = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        super.afterConnectionEstablished(session);
        // 获取参数
        Map<String, Object> attributes = session.getAttributes();
        Object o = attributes.get("pictureId");
        Long pictureId = Long.parseLong(o.toString());
        User user = (User) attributes.get("user");
        // 加入pictureIdSessionSetMap集合
        pictureIdSessionSetMap.putIfAbsent(pictureId, ConcurrentHashMap.newKeySet());
        pictureIdSessionSetMap.get(pictureId).add(session);
        // 构建返回消息
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditTypeEnum.INFO.getValue());
        pictureEditResponseMessage.setMessage(user.getUserName() + "加入编辑");
        UserVO userVO = userService.getUserVO(user);
        pictureEditResponseMessage.setUserVO(userVO);
        // 广播消息
        broadcastMessage(pictureId, pictureEditResponseMessage);
    }

    @Override
    protected void handleTextMessage(@NotNull WebSocketSession session, @NotNull TextMessage message) throws Exception {
        super.handleTextMessage(session, message);
        // 获取参数
        Map<String, Object> attributes = session.getAttributes();
        Object o = attributes.get("pictureId");
        Long pictureId = Long.parseLong(o.toString());
        User user = (User) attributes.get("user");
        // 只能有一个用户在编辑
        Long userId = pictureIdSessionIdMap.get(pictureId);
        if (userId == null) {
            pictureIdSessionIdMap.put(pictureId, user.getId());
        } else {
            // 判断是否是当前用户
            if (!userId.equals(user.getId())) {
                return;
            }
        }
        PictureEditRequestMessage pictureEditRequestMessage = JSONUtil.toBean(message.getPayload(), PictureEditRequestMessage.class);
        // 生产消息，异步执行
        pictureEditEventProducer.pictureEditEventPublish(pictureEditRequestMessage, pictureId, session);
    }

    public void handleActionEdit(Long pictureId , WebSocketSession session, PictureEditRequestMessage pictureEditRequestMessage) {
        String type = pictureEditRequestMessage.getType();
        String editAction = pictureEditRequestMessage.getEditAction();
        PictureEditActionEnum enumByValue = PictureEditActionEnum.getEnumByValue(editAction);
        ThrowUtils.throwIf(enumByValue == null, ErrorCode.PARAMS_ERROR);
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(type);
        pictureEditResponseMessage.setEditAction(editAction);
        User user = (User) session.getAttributes().get("user");
        pictureEditResponseMessage.setMessage(user.getUserName() + "执行了" + enumByValue.getText());
        UserVO userVO = userService.getUserVO(user);
        pictureEditResponseMessage.setUserVO(userVO);
        broadcastMessage(pictureId, pictureEditResponseMessage);
    }

    public void handleExitEdit(Long pictureId, WebSocketSession session, PictureEditRequestMessage pictureEditRequestMessage) {
        String type = pictureEditRequestMessage.getType();
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(type);
        User user = (User) session.getAttributes().get("user");
        pictureEditResponseMessage.setMessage(user.getUserName() + "退出编辑");
        pictureEditResponseMessage.setUserVO(userService.getUserVO(user));
        // 删除pictureIdSessionIdMap集合元素
        pictureIdSessionIdMap.remove(pictureId);
        broadcastMessage(pictureId, pictureEditResponseMessage);
    }

    public void handleEnterEdit(Long pictureId, WebSocketSession session, PictureEditRequestMessage pictureEditRequestMessage) {
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(pictureEditRequestMessage.getType());
        User user = (User) session.getAttributes().get("user");
        pictureEditResponseMessage.setMessage(user.getUserName() + "开始编辑");
        UserVO userVO = userService.getUserVO(user);
        pictureEditResponseMessage.setUserVO(userVO);
        broadcastMessage(pictureId, pictureEditResponseMessage);
    }
    @Override
    public void afterConnectionClosed(@NotNull WebSocketSession session, @NotNull CloseStatus status) throws Exception {
        super.afterConnectionClosed(session, status);
        Map<String, Object> attributes = session.getAttributes();
        Object o = attributes.get("pictureId");
        Long pictureId = Long.parseLong(o.toString());
        User user = (User) attributes.get("user");
        if(Objects.equals(pictureIdSessionIdMap.get(pictureId), user.getId())){
            pictureIdSessionIdMap.remove(pictureId);
        }
        // 删除pictureIdSessionSetMap集合元素
        Set<WebSocketSession> webSocketSessions = pictureIdSessionSetMap.get(pictureId);
        webSocketSessions.remove(session);
        if (CollUtil.isEmpty(webSocketSessions)) {
            pictureIdSessionSetMap.remove(pictureId);
        }
        // 广播离开编辑
        PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage();
        pictureEditResponseMessage.setType(PictureEditTypeEnum.INFO.getValue());
        pictureEditResponseMessage.setMessage(user.getUserName() + "离开编辑");
        UserVO userVO = userService.getUserVO(user);
        pictureEditResponseMessage.setUserVO(userVO);
        broadcastMessage(pictureId, pictureEditResponseMessage);
    }

    /**
     * 广播消息，排除自己
     */
    public void broadcastMessage(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage, WebSocketSession excludeSession) {
        Set<WebSocketSession> webSocketSessions = pictureIdSessionSetMap.get(pictureId);
        if (CollUtil.isNotEmpty(webSocketSessions)) {
            ObjectMapper objectMapper = new ObjectMapper();
            SimpleModule module = new SimpleModule();
            module.addSerializer(Long.class, ToStringSerializer.instance);
            module.addSerializer(Long.TYPE, ToStringSerializer.instance);
            objectMapper.registerModule(module);
            // json序列化
            String string = null;
            try {
                string = objectMapper.writeValueAsString(pictureEditResponseMessage);
            } catch (JsonProcessingException e) {
                log.error("json序列化失败：{}", e);
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "json序列化失败");
            }
            TextMessage textMessage = new TextMessage(string);
            for (WebSocketSession webSocketSession : webSocketSessions) {
                try {
                    // 排除当前编辑者
                    if (webSocketSession.equals(excludeSession)) {
                        continue;
                    }
                    webSocketSession.sendMessage(textMessage);
                } catch (IOException e) {
                    log.error("广播消息失败：{}", e);
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "广播消息失败");
                }
            }
        }
    }

    /**
     * 广播消息给所有用户
     */
    public void broadcastMessage(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage) {
        broadcastMessage(pictureId, pictureEditResponseMessage, null);
    }
}
